/*
 * File:    Try.java
 * Created: 10-Feb-2009
 * Version: $Id$
 *
 * COPYRIGHT (C) 2009, Bitgate Mobile, LLC.  All Rights Reserved.
 *
 * software@bitgatemobile.com
 */

package com.webplasm.render.tags.u;

import java.util.Arrays;
import java.util.List;
import java.util.Map;

import com.webplasm.context.RenderContext;
import com.webplasm.render.engine.RenderException;
import com.webplasm.render.tags.TagInterface;
import com.webplasm.render.tags.WrappedNode;
import com.webplasm.store.VariableStoreInterface;
import com.webplasm.store.VariableContainer.VariableType;

/**
 * This tag allows for error checking to be performed inside a rendered page.  It behaves almost exactly the same
 * way that Java exception handling does.
 * <p/>
 * The expected use of this tag is as follows:
 * <p/>
 * <pre>
 * &lt;u:try var=&quot;error&quot;&gt;
 *     &lt;try&gt; ... some code here that is error-prone or error-sensitive &lt;/try&gt;
 *     &lt;catch&gt; ... execute this body of code when an error happens &lt;/catch&gt;
 *     &lt;finally&gt; ... always execute this body of code after either of the two complete &lt;/finally&gt;
 * &lt;/u:try&gt;
 * </pre>
 * <p/>
 * Please note, the <code>try</code> and <code>catch</code> sub-nodes are requirements.  <code>finally</code> is
 * optional, as it will override any output generated by the <code>catch</code> sub-node.  Also, note that the
 * &quot;var&quot; element in the u:try statement allows for the error message to be caught for display purposes.
 * 
 * @author kenji
 * @since Webplasm 1.0
 */
public class Try extends WrappedNode implements TagInterface {
	private static final String DESCRIPTION = "Allows for a block of Webplasm code to run that may be prone to an error, with safe error handling.";
	
	public String getNamespace() {
		return "u";
	}

	public List<String> getAttributes() {
		return Arrays.asList("var");
	}
	
	public List<String> getManagedNodes() {
		return Arrays.asList("try", "catch", "finally");
	}

	public String getDescription() {
		return DESCRIPTION;
	}

	@Override
	public StringBuffer render(RenderContext rContext) throws RenderException {
		Map<String, WrappedNode> wrappedNodes = prepareNodes(thisNode);
		
		if (wrappedNodes.get("try") == null) {
			throw new RenderException("u:try requires a 'try' node.");
		}
		
		if (wrappedNodes.get("catch") == null) {
			throw new RenderException("u:try requires a 'catch' node.");
		}
		
		// Render order:
		// If no "finally" block is set, and "try" does not fail, "try" returns.
		// If "try" fails, "catch" is returned.
		// If "finally" block is set, "finally" is returned regardless of "try" or "catch" results.
		
		// NOTE: Because this code uses the same exception mechanism that Java does, it was extremely easy to implement
		// a try/catch/finally mechanism below.
		
		try {
			return wrappedNodes.get("try").render(rContext);
		} catch(RenderException ex) {
			if (elements.get("var") != null) {
				VariableStoreInterface vsInterface = rContext.getVariableStore().getVariable(elements.get("var"), VariableType.STRING);

				vsInterface.setValue(ex.getMessage());
			}
			
			return wrappedNodes.get("catch").render(rContext);
		} finally {
			if (wrappedNodes.get("finally") != null) {
				return wrappedNodes.get("finally").render(rContext);
			}
		}
	}
}